'use strict';

(function(fn, w, name) {
    w[name] = fn(w);
    
})(function(w) {
    
    var Tween = {
        linear: function(e, t, a, n) {
            return a * e / n + t
        },
        easeIn: function(e, t, a, n) {
            return a * (e /= n) * e + t
        },
        easeOut: function(e, t, a, n) {
            return - a * (e /= n) * (e - 2) + t
        },
        easeBoth: function(e, t, a, n) {
            return (e /= n / 2) < 1 ? a / 2 * e * e + t: -a / 2 * (--e * (e - 2) - 1) + t
        },
        easeInStrong: function(e, t, a, n) {
            return a * (e /= n) * e * e * e + t
        },
        easeOutStrong: function(e, t, a, n) {
            return - a * ((e = e / n - 1) * e * e * e - 1) + t
        },
        easeBothStrong: function(e, t, a, n) {
            return (e /= n / 2) < 1 ? a / 2 * e * e * e * e + t: -a / 2 * ((e -= 2) * e * e * e - 2) + t
        },
        elasticIn: function(e, t, a, n, r, s) {
            if (0 === e) return t;
            if (1 == (e /= n)) return t + a;
            if (s || (s = .3 * n), !r || r < Math.abs(a)) {
                r = a;
                var c = s / 4
            } else var c = s / (2 * Math.PI) * Math.asin(a / r);
            return - (r * Math.pow(2, 10 * (e -= 1)) * Math.sin(2 * (e * n - c) * Math.PI / s)) + t
        },
        elasticOut: function(e, t, a, n, r, s) {
            if (0 === e) return t;
            if (1 == (e /= n)) return t + a;
            if (s || (s = .3 * n), !r || r < Math.abs(a)) {
                r = a;
                var c = s / 4
            } else var c = s / (2 * Math.PI) * Math.asin(a / r);
            return r * Math.pow(2, -10 * e) * Math.sin(2 * (e * n - c) * Math.PI / s) + a + t
        },
        elasticBoth: function(e, t, a, n, r, s) {
            if (0 === e) return t;
            if (2 == (e /= n / 2)) return t + a;
            if (s || (s = .3 * n * 1.5), !r || r < Math.abs(a)) {
                r = a;
                var c = s / 4
            } else var c = s / (2 * Math.PI) * Math.asin(a / r);
            return 1 > e ? -.5 * r * Math.pow(2, 10 * (e -= 1)) * Math.sin(2 * (e * n - c) * Math.PI / s) + t: r * Math.pow(2, -10 * (e -= 1)) * Math.sin(2 * (e * n - c) * Math.PI / s) * .5 + a + t
        },
        backIn: function(e, t, a, n, r) {
            return "undefined" == typeof r && (r = 1.70158),
            a * (e /= n) * e * ((r + 1) * e - r) + t
        },
        backOut: function(e, t, a, n, r) {
            return "undefined" == typeof r && (r = .70158),
            a * ((e = e / n - 1) * e * ((r + 1) * e + r) + 1) + t
        },
        backBoth: function(e, t, a, n, r) {
            return "undefined" == typeof r && (r = 1.70158),
            (e /= n / 2) < 1 ? a / 2 * e * e * (((r *= 1.525) + 1) * e - r) + t: a / 2 * ((e -= 2) * e * (((r *= 1.525) + 1) * e + r) + 2) + t
        },
        bounceIn: function(e, t, a, n) {
            return a - Tween.bounceOut(n - e, 0, a, n) + t
        },
        bounceOut: function(e, t, a, n) {
            return (e /= n) < 1 / 2.75 ? 7.5625 * a * e * e + t: 2 / 2.75 > e ? a * (7.5625 * (e -= 1.5 / 2.75) * e + .75) + t: 2.5 / 2.75 > e ? a * (7.5625 * (e -= 2.25 / 2.75) * e + .9375) + t: a * (7.5625 * (e -= 2.625 / 2.75) * e + .984375) + t
        },
        bounceBoth: function(e, t, a, n) {
            return n / 2 > e ? .5 * Tween.bounceIn(2 * e, 0, a, n) + t: .5 * Tween.bounceOut(2 * e - n, 0, a, n) + .5 * a + t
        }
    };
    
    function startMove(obj)
    {
        var ele = obj.ele, // 操作对象
            target = obj.target, // 运动目标
            time = obj.iTime,
            type = obj.sType || 'linear', // 动画类型
            callback = obj.fnEnd, // 回调函数
            duringFn = obj.fnDuring, // 在运动过程中执行的函数
            moveTypeFn = Tween[type], // 运动偏移方式
            i = 0,
            cssValue = {}, // 元素 初始状态的值
            diffValue = {}, // 元素初始状态的值，和最终状态的值之间的差值
            startValue = {}, // 初始化差值
            key = '',
            count = time / 24 // 能够执行几次
            ;
            
        clearInterval(ele.timer); // 清除上次的计时器
        for(key in target) {
            cssValue[key] = css(ele, key);
            diffValue[key] = target[key] - cssValue[key];
            startValue[key] = 0;
        }
        if(30 > time) { // 如果小于30毫秒直接到目标值
            for(key in a) {
                css(ele, key, target[key]);
            }
        } else {
            ele.timer = setInterval(function() {
                if(count > i) {
                    i++;
                    for(key in target) {
                        startValue[key] = moveTypeFn(i, cssValue[key], diffValue[key], count);
                        
                        css(ele, key, moveTypeFn(i, cssValue[key], diffValue[key], count));
                    }
                } else {
                    for(key in target) {
                        css(ele, key, target[key]);
                        clearInterval(ele.timer);
                        callback && callback.call(ele);
                    }
                }
                
                duringFn && duringFn.call(ele, cssValue, startMove, i, count);
            }, 24);
        }
    }
    
    function css(ele, key, val)
    {
        if(2 === arguments.length) {
            if( "scale" == key ||
                "rotate" == key ||
                "rotateX" == key ||
                "rotateY" == key ||
                "scaleX" == key ||
                "scaleY" == key ||
                "translateY" == key ||
                "translateZ" == key
            ) {
                switch(ele.$Transform || (ele.$Transform = {}), t)
                {
                    case 'scale':
                    case 'scaleX':
                    case 'scaleY':
                        return "number" == typeof ele.$Transform[t] ? ele.$Transform[t] : 100;
                    case 'translateY':
                    case 'translateX':
                    case 'translateZ':
                        return ele.$Transform[t] ? ele.$Transform[t] : 0;
                    default:
                        return ele.$Transform[t] ? ele.$Transform[t] : 0;
                }
            }
            
            var n = ele.currentStyle ? ele.currentStyle[key] : document.defaultView.getComputedStyle(ele, !1)[key];
            return "opacity" === key ? Math.round(100 * parseFloat(n)) : parseInt(n);
        } else if(3 === arguments.length) {
            switch(key) {
                case "scale" :
                case "scaleX" :
                case "scaleY" :
                case "route" :
                case "routeX" :
                case "routeY" :
                case "translateY" :
                case "translateX" :
                case "translateZ" :
                    setCss3(ele, key, val);
                    break;
                case "width" :
                case "height" :
                case "paddingLeft" :
                case "paddingTop" :
                case "paddingRight" :
                case "paddingBottom" :
                    a = Math.max(val, 0);
                case "left":
                case "top":
                case "marginLeft":
                case "marginTop":
                case "marginRight":
                case "marginBottom":
                    ele.style[key] = "string" === typeof val ? val : val + 'px';
                    break;
                case "opacity" :
                    ele.style.filter = "alpha(opacity:" + val + ")";
                    ele.style.opacity = val / 100;
                    break;
                default:
                    e.style[key] = val;
            }
            
            return function(key, val) {
                css(ele, key, val);
            }
        }
    }
    
    function setCss3(ele, key, val)
    {
        var sKey = "";
        var str = "";
        var beforeFix = ['Webkit', 'Moz', 'O', 'mx', ''];
        ele.$Transform || (ele.$Transform = {});
        ele.$Transform[t] = parseInt(val);
        
        for(sKey in ele.$Transform) {
            switch(sKey) {
                case "scale" :
                case "scaleX" :
                case "scaleY" :
                    str += sKey + "(" + ele.$Transform[sKey]/100 + ") ";
                    break;
                case "rotate" :
                case "rotateX" :
                case "rotateY" :
                    str += sKey + "(" + ele.$Transform[sKey] + "deg)";
                    break;
                case "translateY" :
                case "translateX" :
                case "translateZ" :
                    str += sKey + "(" + ele.$Transform[sKey] + "px)";
            }
        }
        
        for(var c = 0; c < beforeFix.length; c++) {
            ele.style[beforeFix[c] + "Transform"] = str;
        }
    }
    
    return startMove;
}, window, 'startMove');